
#include "gds.hpp"
#include "../ly.hpp"

#include <spdlog/spdlog.h>
#include <gdstk/gdstk.hpp>

namespace ly {

ly::Points to_points(const gdstk::Array<gdstk::Vec2>& point_array) {
  ly::Points points;
  gdstk::Vec2* v_p = point_array.items;
  for (uint64_t k = 0; k < point_array.count; k++) {
    points.push_back(ly::Point(v_p->x, v_p->y));
    v_p++;
  }
  return points;
}
ly::Polygon to_poly(gdstk::Polygon* poly) {
  return ly::Polygon(to_points(poly->point_array));
}

// 读取属性
ly::properties_id_type read_prop(gdstk::Property* property, ly::Properties& rep) {
  ly::Properties::properties_set props_set;
  while (property) {
    uint64_t len = strlen(property->name);
    gdstk::PropertyValue* value = property->value;
    while (value) {
      if (value->type == gdstk::PropertyType::String) {
        std::string value_str(reinterpret_cast<const char*>(value->bytes), static_cast<size_t>(value->count));
        props_set.insert(std::make_pair(rep.prop_name_id(ly::Variant(property->name)), ly::Variant(value_str)));
      }
      value = value->next;
    }
    property = property->next;
  }

  auto p_id = rep.properties_id(props_set);
  return p_id;
}

void gdstk_to_ly(gdstk::Library* lib, GdsParser::layout_sptr lyt) {
  //   auto lyt = ly::Layout::create();
  lyt->dbu(lib->unit / lib->precision);
  ly::Properties::properties_set layout_props;

  gdstk::Cell** p_cell = lib->cell_array.items;
  const uint64_t c_size = lib->cell_array.count;
  for (uint64_t i = 0; i < lib->cell_array.count; i++) {
    gdstk::Cell* cell = *p_cell++;
    SPDLOG_INFO("Cell: {}", cell->name);

    // 创建cell
    if (!lyt->is_exist_cell(cell->name)) {
      lyt->add_cell(cell->name);
    }

    auto t_cell = lyt->cell(cell->name);

    // 读取属性
    // auto prop_id = read_prop(cell->properties, lyt->properties());

    SPDLOG_INFO("poly : {}, path: {}, label: {}, ref: {}", cell->polygon_array.count, cell->flexpath_array.count, cell->label_array.count,
                cell->reference_array.count);

    gdstk::Polygon** poly_p = cell->polygon_array.items;
    for (uint64_t j = 0; j < cell->polygon_array.count; j++) {
      gdstk::Polygon* poly = *poly_p++;
      // add poly
      auto shape = t_cell->shapes(gdstk::get_layer(poly->tag));
      auto e = shape->create_entity();
      shape->emplace_or_replace(e, ly::Polygon(to_points(poly->point_array)));

      // 添加属性组件
      auto prop_id = read_prop(cell->properties, lyt->properties());
      shape->emplace_or_replace(e, ly::ObjectWithProperties(prop_id));
    }

    gdstk::ErrorCode error_code;

    gdstk::FlexPath** path_p = cell->flexpath_array.items;
    for (uint64_t j = 0; j < cell->flexpath_array.count; j++) {
      gdstk::FlexPath* path = *path_p++;
      // t_cell->shapes(gdstk::get_layer(path->tag)).add(ly::Path(to_points(path->point_array)));

      if (path->simple_path) {
        if (path->spine.point_array.count > 1) {
          gdstk::FlexPathElement* el = path->elements;
          for (uint64_t ne = 0; ne < path->num_elements; ne++, el++) {
            // add path
            auto shapes = t_cell->shapes(gdstk::get_layer(el->tag));
            auto e = shapes->create_entity();
            shapes->emplace_or_replace(e, ly::Path(to_points(path->spine.point_array)));

            auto prop_id = read_prop(path->properties, lyt->properties());
            shapes->emplace_or_replace(e, ly::ObjectWithProperties(prop_id));
          }
        }
      }
    }

    auto insts = lyt->cell(cell->name)->instances();
    gdstk::Reference** ref_p = cell->reference_array.items;
    for (uint64_t j = 0; j < cell->reference_array.count; j++) {
      gdstk::Reference* ref = *ref_p++;

      if (!lyt->is_exist_cell(ref->cell->name)) {
        lyt->add_cell(ref->cell->name);
      }

      auto e = insts->create_entity();
      insts->emplace_or_replace(e, ly::CellInst(lyt->cell(ref->cell->name)->id()));

      // 矩形阵列
      if (ref->repetition.type == gdstk::RepetitionType::Rectangular) {
        insts->emplace_or_replace(e, ly::RectangularArray(ref->repetition.columns, ref->repetition.rows,
                                                          ly::RectangularArray::point_t{ref->repetition.spacing.x, ref->repetition.spacing.y}));
      }
      bool is_mag = false;
      if (std::fabs(ref->magnification - 1.0) > 1e-9) {
        is_mag = true;
      }

      // 检查角度是否为90度倍数
      double angle_deg = ref->rotation;
      double a = angle_deg / 90.0;
      if (a < -4 || a > 4) {
        SPDLOG_WARN("Invalid rotation angle (%g is less than -360 or larger than 360)", angle_deg);
      }
      int angle = int(a < 0 ? (a - 0.5) : (a + 0.5));
      if (fabs(double(angle) - a) > 1e-9) {
        angle = -1;  // indicates arbitrary orientation. Take angle_deg instead
      } else {
        if (angle < 0) {
          angle += ((4 - 1) - angle) & ~(4 - 1);
        }
        angle = angle % 4;
      }

      // 复杂变换
      if (is_mag || angle < 0) {
        // 添加复杂变换组件
        insts->emplace_or_replace(
            e, ly::CplxTrans(ly::CplxTrans::point_t(ref->origin.x, ref->origin.y), ref->magnification, ref->rotation, ref->x_reflection));
      } else {
        // 添加简单变换组件
        insts->emplace_or_replace(e, ly::Trans(angle, ref->x_reflection, ly::Trans::point_t(ref->origin.x, ref->origin.y)));
      }

      // if (ref->repetition.type == gdstk::ArrayType::Regular) {}
      // if (ref->magnification != 1.0) {}

      // 添加属性
      auto prop_id = read_prop(ref->properties, lyt->properties());
      insts->emplace_or_replace(e, ly::ObjectWithProperties(prop_id));

      SPDLOG_INFO("ref id: [{}] origin: [{}, {}]", j, ref->origin.x, ref->origin.y);
    }

    // for(auto [])
    insts->print_all();
  }

  SPDLOG_INFO("Done");
}

void GdsParser::read_from_gdstk(const char* file, GdsParser::library_sptr lib) {
  gdstk::ErrorCode error_code;
  gdstk::Set<gdstk::Tag> shape_tags = {};
  auto gdstk_lib = gdstk::read_gds(file, 1e-6, 1e-9, nullptr, &error_code);
  gdstk_to_ly(&gdstk_lib, lib->layout());
}
}  // namespace ly